{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "### 数据处理"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# 安装类库\n",
    "# !mkdir /home/aistudio/external-libraries\n",
    "# !pip install imgaug -t /home/aistudio/external-libraries\n",
    "import sys\n",
    "sys.path.append('/home/aistudio/external-libraries')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "image shape: (32, 32, 3)\n",
      "label value: horse\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAMgAAADFCAYAAAARxr1AAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4zLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvIxREBQAAHC1JREFUeJztnXuMXHd1x79nZu689732erO24zixnZg8XGEgFNTybAOqFKhaBH9U+SMCKoFUBP9EVGqp1EpUKqD+UVEFNSKVKIEWUFIaKCGiStNAHiTEeZgkdmJnba937X3NzO687+kfMw47+z17Pdldr3fN+UjW7h7fufd378yZe89bVBWO49jELvcCHGcz4wriOBG4gjhOBK4gjhOBK4jjROAK4jgRuII4TgSuII4TwZoURERuE5GXROSYiNy1XotynM2CrDaSLiJxAC8D+CCAUwCeBPAJVX1xpdfEg4QGqaBDlslkaLtYTEhWqVRIptowjxOG/Pp4LE6yIJkgWT7XY+2RJLXFKskaTd5OY/b1jRtfTf2D20iWyeR4NSHvs1xeNGQLJEun+XpjhY9Ao9kkWRAEJEum0vYO6DB8IDWurSrLwpBlANA0rnmjXiNZPN75/k9PnkepUOQPyjL4E9I9bwdwTFVfBQARuQ/A7QBWVJAgFWDnjXs7ZDfd9BbaLpNJkuyV40dJVq/NmseplPj1OeODv2v3MMnecevvkUxCvuCnnjlOsvOzJZI1svxaAMjz5x5//PE/J9lbbnonycqLvM8jzz9FsueeY9nBA3y9LYUDgNm5Asm2je4k2dV795FMhL8B6k3+UqmDv/iqTVbshcWyucbSLMunz46TrL833/H3333+S+b+lrOWR6wxAEtXcqot60BEPiUiT4nIU806fyM5zmbmkhvpqnq3qh5W1cPxgB9zHGczs5ZHrNMAdi35e2dbtiIahqhXOm+JszPnaLvkju0kGx1h2bmpunmcuXN8i85m2V5ZWJwh2anTL5NsbIRujEinUyRTnSfZ4NCAuca3HtrPxxkdJVnTeJ6ePc/X7OWjL5FsboYfkRYW+HGqt99eY98wPwdWQ76O52fPkyyMZ0kmYtkLbDtVq/yoWq7aj6qLNd5nkOZ1yzL756LGR5u13EGeBLBPRK4RkSSAjwN4YA37c5xNx6rvIKraEJHPAvhvAHEA96jqC+u2MsfZBKzlEQuq+iCAB9dpLY6z6fBIuuNEsKY7yJslnohjaLivQ1avsh87YQTXioU5klWMeAAADA8MsWw4T7KbbmGfPowg5dnJUyTLBmykZ7MchGs07DWm0xyrSSX42DHl2EEixobywQPXkWz3VVeRLNfDwcggawVHgcoiOx1qjSLJ5ubZ2TGzwO+X5XAIhM9lscD7q4Z2iKBoxA8b81Mkyy6Lt1SNwLOF30EcJwJXEMeJwBXEcSJwBXGcCDbUSE+nUrju2j0dsiDJ6SepFMvm56dJ1qzb2bxqJBcOD/WT7PoDe0k2PcsJkM8+8wqvMdhBsr7+XpIVY2ysAnZUOQZedyrBstERjhTnMntI9sJznBXQk+HM2zDJDgcAiNVYnkuwsZxK8vdsocTnVyxwhoOEbCxXipwBsNiwsyZmjIzjsMjv4WK9MzrfWOGzsxy/gzhOBK4gjhOBK4jjROAK4jgRbKyRnk7hwP5rO2S5HEef6w023IoFjo42a3Z0dWqCX79jhKsHBw2jenqanQGJBDsNjII5hIYhGQ/s76Aw5G1j4KyCVJIj6VYFYCPNsljIr80aRvZ8jVPOAaA4z8ZyT9pwLoR8jqkGy3LCH7eZeY7MWxF3GE4NAIgpX8dymdPlK4VOw90qJzb339VWjvNbiiuI40TgCuI4EbiCOE4EriCOE8GavFgicgJAEUATQENVD19ke6SWNWtLpXgJVg+rgzccIFk6sJd/8jinGoyNctOHIMGvtxrMZY30jHiTv1uGhvpIFvbY3pJUiutBmk32vtSqRhO8uLFuw0OUCtjzk0/yeqbO83EB4PzZsyyrsmercJZ7dZQWjfUY1/HE+DGS7drDdSw92zlVCADiFfZ4FYxGIPW5ztqWZqO7VJP1cPO+V1W5rYXjXAH4I5bjRLBWBVEAPxGRX4rIp6wNlnZWXCjZASnH2ays9RHr3ap6WkS2A3hIRH6tqo8s3UBV7wZwNwCM7R71mdPOlmKtbX9Ot39OicgP0Gpo/chK28cTCfQNdqZ81BucXlGscPrBVbvYcOtL2c0GqsUTJBscYCMvFuMbaCbLxvPAIDd86AlZtnPn1SQLM7YxmO8zajCMdn+lBW6cEDcaSzRqnLJjpchUjQ6FzzzxS3ONP3/61ySrL/B7M33mdX5xjGtWtg9xus/4aTbSc3l+X7btto30nGFsx4x6oHBZR8hupxqs+hFLRHIi0nPhdwB/AOD51e7PcTYja7mDjAD4gYhc2M+/qeqP12VVjrNJWEvr0VcB3LKOa3GcTYe7eR0ngg2tB4kl4sgNDXbIzkycoO3OzXPccWgHt+iPiz36K2M0Ieg3WvxncmyQDxl1I8UFrqvIGEb60DAfI8jbl1gDNi6TaXY61OtsFFtdAWtlNtwLJa6VeOKJX5DsJw/afpXZOTZ2G4aRX2uywRsPeLuRPv4+Tjf5PTh/hiP4e27gJhkA0GvU2+SM0XoVGvV2iY10x/ltwBXEcSJwBXGcCFxBHCeCDTXSIYBkOtOtYxk2LhNGNHtukY1VNYxDAIgbowV6BzgSn+/laO9rpzhV/twkp4hnhQ3gA9ewkT02yvMNAWCxyeeTSPB6YtY45TpHyE+f4pEBD/2EDfJnn+Xo+My0nSOXTnH6ftMYV2AFpStlw7mwwGn1mRjPXS+e59eePn7GXOPYGI9zyKZ4PuJcrHOf1phqC7+DOE4EriCOE4EriONE4AriOBFsbCQ9rgjyncbtjt2DtF3/do5Sq5XWXLaN9FyMU6Mnpzmye+Qod2t87DFOSD53miP7KWXjcnGGL+d7/9AeLbDnwC6SBXHeJ2KcLTB+jlPgf/xfPyfZIz/jNPZa0xgjYJwLANTrvG29wVkFVaM8oVbj2vXZWXYu5AM+v1jduLbG+wcAsxk+djLJRnoq0/mZEqPUwcLvII4TgSuI40TgCuI4EbiCOE4EFzXSReQeAH8EYEpVb2zLBgF8B8AeACcAfExVOQS9jFAbqDQ6N0ulOXocpA0jK8HGbio0irgBLM5wJP34S9xM7MXnx0k2foKN0ECHSDY7x4byo2d/xdsVuOYeAMb2cvp2b5bPJybsiDh69FWSPf5/L5Bs0QiQJ1NsFDdWyEioGvMDq1WeM1iu8BzGWIJfO7c4SbIwxSMotvezAyMd8GcCAAaG2MlTNcZQ9IWd6e4Jo2mgRTd3kG8CuG2Z7C4AD6vqPgAPt/92nCuOiypIu43P8kSf2wHc2/79XgAfWed1Oc6mYLU2yIiqTrR/P4tWAweTpY3jinN8e3aczcyajXRtNRhasX5RVe9W1cOqerinn+0Nx9nMrDaSPikio6o6ISKjADgkbdBo1HFuqrMT+LZhTkNv1NlYLRr914KQI7MAcPQZnjOICjd1G+jlVPT5XjbSk0Y0e7ZiNG9juxTT5+xU8mdffJRkxblTJDO/wYzIdyLOqfZ9vbzuUDl1X41GawBQq7ODoW7Jmvxk8NZ33UiyXaOjJHv28SMkKzU5Cl+Hvcbd+/k9jBkTA0bLne/rI//5U3N/tK+utmIeAHBH+/c7ANy/yv04zqbmogoiIt8G8HMAB0TklIjcCeDLAD4oIq8A+ED7b8e54rjoI5aqfmKF/3r/Oq/FcTYdHkl3nAg2NN292WiiNNtpgMUavIRGg0eHzc5w5Lo4YxtuR37BUfMbr+URbHWjIdz0JDct6+/lZnK5PHdTLysb5PkcR3oBIDHN9dnlEn9fNRt8jrkcHzubZsM9k+GMgmKRzzkmfL0BIJ1mI79mOFD6hrk84a3vexvJ9h+4hmQDu7jZ3q+feY1khYadqCF5jppXxYjilzqj/U3tbgSb30EcJwJXEMeJwBXEcSJwBXGcCFxBHCeCjfVi1ZuYmez0JhSmOU2hYTRomJpkz1S5YKeaTIyzx6t6/imShVVOISsWjayZkNMzrtrOnq258+zFajT4tQDQ28OvX+znGpGFItdaiPG2NZrs7Yon2OMUGKMhEgl7jERvH3vB4lPcJGH3wb0ku/7wQZJl8vx+Hfr9m0m2fYzTj8oLdqJrWYzmEMZcx3OLndexERrNKwz8DuI4EbiCOE4EriCOE4EriONEsKFGepAMMDq2s0MWj/MSyiVOw0iBDbyzdTbQAKBhGHQnTj7N60kY8+16uYlAIsWGdt0YX1AqcR1KKrXfXOPeEa5PqRsNERp1TgOxjO+E0RwxNL7+8oOcFjKQ5RECAJBO8XGu2suG++9+6ADJ9ozy6ISGsmGsed5fj5XGU7WbX4QBf1Z6E7zPhnTuM2FcQwu/gzhOBK4gjhOBK4jjROAK4jgRrLaz4pcAfBLAhfD2F1X1wYvtK5VO49r9+zr3H2PjOy28rJRRsjD+GnfqA4AnHzLqCco8w8/q+t8/xMZlT54N26mJCZLVa5wB0GzatRbWPnft3E0y6xtMwcauGDUdxQWOwvf0sAE8Mmwb6YFxfXbdzI0Xxq7jcRNiRPaTcX6v40k+iHXNYkn7OlaMcQ75OGdIxJcdO2E4hyxW21kRAL6mqofa/y6qHI6zFVltZ0XH+a1gLTbIZ0XkiIjcIyJcN9lmaWfFwqx3VnS2FqtVkK8DuBbAIQATAL6y0oZLOyv2DnhnRWdrsapIuqq+YR2LyDcA/LCb1zWbTRSLnZHPhGGklSps4KWNsPDcrB1Jr1aM2Xp1Y8ahFUnPGI0KFrgJwNkz3NxBjZl+J4+fNNdYNjoz5rOcij62g9PiFRzZr1Q51f7ka6+QLJviYyyk7Sfo/DB/oSX7uIPjbJmPrWU2qhPGDMbAMNyThndAQnuOYgzGWITQyD6QZe9/d4H01d1B2u1GL/BRADz50nGuALpx834bwHsADIvIKQB/DeA9InIIrabVJwB8+hKu0XEuG6vtrPgvl2AtjrPp8Ei640SwoenuoSrK1U6DN6yyAZxPGnP0jLrwhUU7BbpaZWO5abzeqhefL7DBGoYcmZ2b5/mGFcNYDWPcyRAAEkZkuGGk2sfFqivnt21+jjsPnp/kaH82zt+J9Yp9Hfvi7L3fBc4ACMt8fZqGoZywMgCMqHnGSFdvjaFhmgHLJcay2LKMDVFPd3ecNeMK4jgRuII4TgSuII4TwYYa6QAgyyKnWaPFfjpgIy2jrMuJuDGLEECzueJM0Q7qxmiB6WmOkI/tYsP0lrdxczOJs+F3Zpwb3gHAyVNPkiyTYaO4boyCSKf4mtWNKH61xpkGxTmOpDcCY7gigL4UR6nDODs2qg2+3g3DSA/rfL1jhpFeEz6X8qK9Rg34mieTnDWRS3duZzleLPwO4jgRuII4TgSuII4TgSuI40SwoUa6GpH0uhqpyYb9FMTZcIdRzw4AYjSZswKxahiSuTwbwB/90w+QbO8NbKTHU7zGI0+9YK7xpz96lGTnjbmFzTobxc2YUfsOI3KdYdlChQ337UOcUg8AB27m5na9/Wy4F6rcRM+aAVirsaFdN0obrBT4utHxHwDCqpXaztkL1WWN4pordN1fjt9BHCcCVxDHicAVxHEicAVxnAi6qSjcBeBfAYygVUF4t6r+o4gMAvgOgD1oVRV+TFXtae9tFECITmu50WTjS40UbyvwWavahlutyoafsUuI8fWwbz+PE9t/kLuXZ4b50tWUDb/D736Hucb9+99CsvEzZ0h25ixH9qF8bA35BH90/0Mkm3iZR8wNjnCaPQBs38kN4RZKPN4sbPJ5xwI2nkWMN9H4BBaMLvdN4xgAkBI26GOGR6Za7vysrGckvQHgC6p6EMCtAD4jIgcB3AXgYVXdB+Dh9t+Oc0XRTeO4CVV9uv17EcBRAGMAbgdwb3uzewF85FIt0nEuF2/KBhGRPQB+B8DjAEZU9ULJ2lm0HsGs17zROG5hnivuHGcz07WCiEgewPcAfE5VO6JN2qqHNB/qljaOy/UZPYwcZxPTlYKISICWcnxLVb/fFk9e6I/V/mkMGHecrU03XixBq83PUVX96pL/egDAHQC+3P55fxf7QrC8a55RQ9E00k+MjAvMTdu9fms1ozmA0azAcm3tNobYZ1PcYXBhntNCakZjiOQKLfwG8lz70bOfuxaODA2SzPLAGGUjeOx/HiPZuOE17OmxuxbmjIYIzRKncRiTF8y0m5rR/TFjdVFM8zWrGesGgIRx4mJ8fpZ7T1d44OH9d7HNuwD8GYDnRORXbdkX0VKM74rInQBOAvhYV0d0nC1EN43jHsXKnUzfv77LcZzNhUfSHScCVxDHiWBjmzaoUlqC1cp/ocx6qyHXaVTmbUMrtOYCxvgpMWs0Jbj6Kp7BF6vwGmWBj50MOe0hZRW3AAgCdiRk49wcIhXjc6k0uKaj1GDjOWk4NpJxbtpwwzVj5hr3DhqpJkleY8nocDlvzGusFK2RCMZ7ZaSkiJUXBHPSAerG3MJmo3ON4QqdGpfjdxDHicAVxHEicAVxnAhcQRwngg1v2tCsdBqTGhhjCWrGDL4iyyZP210L1Yg0G+US6O/rI9mQIZs7a3RwDPnSpRNs9DcadkfAWNwwqo13I6izAVwpcwaBGk0S1Oh4GBhNKQa28zkDQDrNC4oLG/mG/wN9yhHyoQbLJie43qVhOBwq4QpNGwzj3TL8q6Vl74OVemDgdxDHicAVxHEicAVxnAhcQRwngg010mMKpJbZ2rGAo89loxnDzAQb5Oen7BIUK7PS6heQy7JRvVBkA/i1l06QLBnj1w71cYfChDEvDwDCpJFKXj1PMjWM/KqVX57hlPy48NubyhkNH8RuiLBY4JR+VTbSYaXQxzMkS6d5jWEPOwikOMfHLdtGdWG58Q0gnTQyMYqdn7NY02cUOs6acQVxnAhcQRwnAlcQx4lgLZ0VvwTgkwAuWM9fVNUHo/YVgyAbdkZTa2WOmqLE6cqL57ijX21hhSi1YaaLUX8exI3uiGXepxjp94UK11c3Cmzs9vWyYQoAkjVq8RdneJ81Pk49yfXe8STXs4sR7e/NGPMf67YBXJ/itHoFG+lqpOTPhvwe1ow5ijEjWp8O2cCPG+cMAIkGX8d4jR0g8WrnumPanZHejRfrQmfFp0WkB8AvReRCT8uvqeo/dHUkx9mCdFOTPgFgov17UUQudFZ0nCuetXRWBIDPisgREblHRLiPDTo7K5YK3lnR2VqspbPi1wFcC+AQWneYr1ivW9pZMd/rnRWdrUVXkXSrs6KqTi75/28A+OFFd9QEYsuCs0GCI+m94LrnoMLR1WqJU8EBIB5jvQ+SbFymAjb80sZ2PSlOES812blQmOdZfcV5eyJEIsavv26Uo8qZPF+LQoGj/fNnOKtgbpaN7IEcG8ADwjIAyC4YWQ7GTMHQmIVYXp4yAaBujJRU4/2PG3XziRWy061ov3FpkdTO91pW7GS1bF8X22ClzooX2o62+SiA57s6ouNsIdbSWfETInIILdfvCQCfviQrdJzLyFo6K0bGPBznSsAj6Y4TwcamuyOGnHYanaEx1y+RZG/X7iFeal/uFfM4QYIN46TRRTxhDKxfWGCjr1nj6HqQYMO2Z4Cj2aFRXw0AiwuGgyHPRnoixw6CsMIW6+lxNtKnZ9mxMTy8m2SBER0HgGST5ZUqn0+xzBkAlV6+tknj+sTT/L7US+wIaNTt62hlJKiRYLE84N5d2zi/gzhOJK4gjhOBK4jjROAK4jgRbLiRntJOAzxmdD/XGhtuuYANvH3X7jOP0zQisePjp0lWKnDk+9grx3h/Rl14Is7G8+g2zuHsMYxsANi2nUer1ZJs+JeEjV1N83YNI50/leXtakaWQV3sVHKNG6n6Rnc7aXBkv17kKH4s4NcGxrp7jcyFecNRAgDxPl57o2o0I5xflmpvHNfC7yCOE4EriONE4AriOBG4gjhOBK4gjhPBxs4oBKDxi8f800b6wZ69O0nWv2ObeYx3LrLH49H/fYxkrx1nj1WtZgyhr3KzgWrIqQ+v13i7dMZO45DcdSTLp4zzMboEJvvZO9U7xJ6fwSE+9sIie5ymZ20P0eCeHSQLA15PptpLsqDGXqJKhWVVNeYJZvlDsWCMdwCAsuGMSuc5VWm5E1SsmQ0GfgdxnAhcQRwnAlcQx4mgm5LbtIg8ISLPisgLIvI3bfk1IvK4iBwTke+IrBCOdZwtTDdGehXA+1S11G7e8KiI/AjA59FqHHefiPwzgDvR6nSyIqqKeqPT2LLqNNIpNi6DGG+X7eGGBgCwQ1nvt/fdRrLx1zn9pDfPKS1Tr79Gsvl5rrUI8mw8V0N7tEDd8E5UjHmEGuO3qFTiFJmEkbJz4/XXkKxvgJ0dg/08tgEApit8nB1Xs+FeOM0Oi8lZHuVQqLOhXTMcMlpnAzrM2N/lqQR/VqZnuT7l9MlTHX9XalxzYnHRO4i2uFBFFLT/KYD3AfiPtvxeAB/p6oiOs4XoygYRkXi7YcMUgIcAHAcwp78ZrXoKK3RbXNo4rlBiF6PjbGa6UhBVbarqIQA7AbwdwPXdHmBp47jevN3I2XE2K2/Ki6WqcwB+BuCdAPpF3pjxtRMAP9A7zhanm/EH2wDUVXVORDIAPgjg79FSlD8BcB+AOwDcf9GjqSBe7zxkLMFLiBk1CzWjaD9pl1pAjOHyO4c4Sj02OEKywjyPWehL8P5mZqdJVrIaCxj1EwAQGl9NWmFDu2p0MkwbcwL7R7gZw/49fKOvG0a/Vm2DNd/LDot8H1/06gzXrFTBEfIzU5MkKxmG++AY18oEeTbmASAEX/OUUQ/UM9DZOjpuNOyw6MaLNQrgXhGJo3XH+a6q/lBEXgRwn4j8LYBn0Oq+6DhXFN00jjuCVkf35fJX0bJHHOeKxSPpjhOBK4jjRCCq3faYW4eDiZwDcBLAMAAOtW5N/Fw2Jxc7l6tV1a6XWMKGKsgbBxV5SlUPb/iBLwF+LpuT9ToXf8RynAhcQRwngsulIHdfpuNeCvxcNifrci6XxQZxnK2CP2I5TgSuII4TwYYriIjcJiIvtUt179ro468FEblHRKZE5PklskEReUhEXmn/HIjax2ZBRHaJyM9E5MV2KfVftOVb7nwuZVn4hipIO+HxnwB8CMBBtCblHtzINayRbwJYXrt7F4CHVXUfgIfbf28FGgC+oKoHAdwK4DPt92Irns+FsvBbABwCcJuI3IpW1vnXVPU6ALNolYW/KTb6DvJ2AMdU9VVVraGVKn/7Bq9h1ajqIwCWFzzfjlbJMbCFSo9VdUJVn27/XgRwFK2q0C13PpeyLHyjFWQMwPiSv1cs1d1CjKjqRPv3swC4yGSTIyJ70MrYfhxb9HzWUhYehRvp64i2fOZbym8uInkA3wPwOVXtmHqzlc5nLWXhUWy0gpwGsGvJ31dCqe6kiIwCQPsnz2PepLTbOH0PwLdU9ftt8ZY9H2D9y8I3WkGeBLCv7V1IAvg4gAc2eA3rzQNolRwD3ZYebwJERNCqAj2qql9d8l9b7nxEZJuI9Ld/v1AWfhS/KQsHVnsuqrqh/wB8GMDLaD0j/uVGH3+Na/82gAkAdbSeae8EMISWt+cVAD8FMHi519nlubwbrcenIwB+1f734a14PgBuRqvs+wiA5wH8VVu+F8ATAI4B+HcAqTe7b081cZwI3Eh3nAhcQRwnAlcQx4nAFcRxInAFcZwIXEEcJwJXEMeJ4P8BDOlRG0/6AGEAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<Figure size 216x216 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import paddle\n",
    "import numpy as np\n",
    "from PIL import Image\n",
    "import matplotlib.pyplot as plt\n",
    "import imgaug as ia\n",
    "import imgaug.augmenters as iaa\n",
    "\n",
    "# 读取数据\n",
    "reader = paddle.batch(\n",
    "    paddle.dataset.cifar.train10(),\n",
    "    batch_size=8) # 数据集读取器\n",
    "data = next(reader()) # 读取数据\n",
    "index = 4 # 批次索引\n",
    "\n",
    "# 读取图像\n",
    "image = np.array([x[0] for x in data]).astype(np.float32) # 读取图像数据，数据类型为float32\n",
    "image = image * 255 # 从[0,1]转换到[0,255]\n",
    "image = image[index].reshape((3, 32, 32)).transpose((1, 2, 0)).astype(np.uint8) # 数据格式从CHW转换为HWC，数据类型转换为uint8\n",
    "print('image shape:', image.shape)\n",
    "\n",
    "# 图像增强\n",
    "# sometimes = lambda aug: iaa.Sometimes(0.5, aug) # 随机进行图像增强\n",
    "# seq = iaa.Sequential([\n",
    "#     sometimes(iaa.CropAndPad(px=(-4, 4))),      # 随机裁剪填充像素\n",
    "#     iaa.Fliplr(0.5)])                           # 随机进行水平翻转\n",
    "# image = seq(image=image)\n",
    "\n",
    "# 读取标签\n",
    "label = np.array([x[1] for x in data]).astype(np.int64) # 读取标签数据，数据类型为int64\n",
    "vlist = [\"airplane\", \"automobile\", \"bird\", \"cat\", \"deer\", \"dog\", \"frog\", \"horse\", \"ship\", \"truck\"] # 标签名称列表\n",
    "print('label value:', vlist[label[index]])\n",
    "\n",
    "# 显示图像\n",
    "image = Image.fromarray(image)   # 转换图像格式\n",
    "image.save('./work/out/img.png') # 保存读取图像\n",
    "plt.figure(figsize=(3, 3))       # 设置显示大小\n",
    "plt.imshow(image)                # 设置显示图像\n",
    "plt.show()                       # 显示图像文件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "train_data: image shape (128, 3, 32, 32), label shape:(128, 1)\n",
      "valid_data: image shape (128, 3, 32, 32), label shape:(128, 1)\n"
     ]
    }
   ],
   "source": [
    "import paddle\n",
    "import numpy as np\n",
    "import imgaug as ia\n",
    "import imgaug.augmenters as iaa\n",
    "\n",
    "# 训练数据增强\n",
    "def train_augment(images):\n",
    "    # 转换格式\n",
    "    images = images * 255 # 从[0,1]转换到[0,255]\n",
    "    images = images.transpose((0, 2, 3, 1)).astype(np.uint8) # 数据格式从BCHW转换为BHWC，数据类型转换为uint8\n",
    "    \n",
    "    # 增强图像\n",
    "    sometimes = lambda aug: iaa.Sometimes(0.5, aug) # 随机进行图像增强\n",
    "    seq = iaa.Sequential([\n",
    "        sometimes(iaa.CropAndPad(px=(-4, 4))),      # 随机裁剪填充像素\n",
    "        iaa.Fliplr(0.5)])                           # 随机进行水平翻转\n",
    "    images = seq(images=images)\n",
    "    \n",
    "    # 减去均值\n",
    "    mean = np.array([0.4914, 0.4822, 0.4465]).reshape((1, 1, 1, -1)) # cifar数据集通道平均值\n",
    "    stdv = np.array([0.2471, 0.2435, 0.2616]).reshape((1, 1, 1, -1)) # cifar数据集通道标准差\n",
    "    \n",
    "    images = (images/255.0 - mean) / stdv # 对图像进行归一化\n",
    "    images = images.transpose((0, 3, 1, 2)).astype(np.float32) # 数据格式从BHWC转换为BCHW，数据类型转换为float32\n",
    "    \n",
    "    return images\n",
    "\n",
    "# 验证数据增强\n",
    "def valid_augment(images):\n",
    "    # 转换格式\n",
    "    images = images * 255 # 从[0,1]转换到[0,255]\n",
    "    images = images.transpose((0, 2, 3, 1)).astype(np.uint8) # 数据格式从BCHW转换为BHWC，数据类型转换为uint8\n",
    "    \n",
    "    # 减去均值\n",
    "    mean = np.array([0.4914, 0.4822, 0.4465]).reshape((1, 1, 1, -1)) # cifar数据集通道平均值\n",
    "    stdv = np.array([0.2471, 0.2435, 0.2616]).reshape((1, 1, 1, -1)) # cifar数据集通道标准差\n",
    "    \n",
    "    images = (images/255.0 - mean) / stdv # 对图像进行归一化\n",
    "    images = images.transpose((0, 3, 1, 2)).astype(np.float32) # 数据格式从BHWC转换为BCHW，数据类型转换为float32\n",
    "    \n",
    "    return images\n",
    "\n",
    "# 读取训练数据\n",
    "train_reader = paddle.batch(\n",
    "    paddle.reader.shuffle(paddle.dataset.cifar.train10(), buf_size=50000),\n",
    "    batch_size=128) # 构造数据读取器\n",
    "train_data = next(train_reader()) # 读取训练数据\n",
    "\n",
    "train_image = np.array([x[0] for x in train_data]).reshape((-1, 3, 32, 32)).astype(np.float32) # 读取训练图像\n",
    "train_image = train_augment(train_image)                                                       # 训练图像增强\n",
    "train_label = np.array([x[1] for x in train_data]).reshape((-1, 1)).astype(np.int64)           # 读取训练标签\n",
    "print('train_data: image shape {}, label shape:{}'.format(train_image.shape, train_label.shape))\n",
    "\n",
    "# 读取验证数据\n",
    "valid_reader = paddle.batch(\n",
    "    paddle.dataset.cifar.test10(),\n",
    "    batch_size=128) # 构造数据读取器\n",
    "valid_data = next(valid_reader()) # 读取验证数据\n",
    "\n",
    "valid_image = np.array([x[0] for x in valid_data]).reshape((-1, 3, 32, 32)).astype(np.float32) # 读取验证图像\n",
    "valid_image = valid_augment(valid_image)                                                       # 验证图像增强\n",
    "valid_label = np.array([x[1] for x in valid_data]).reshape((-1, 1)).astype(np.int64)           # 读取验证标签\n",
    "print('valid_data: image shape {}, label shape:{}'.format(valid_image.shape, valid_label.shape))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "### 模型设计"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "import paddle.fluid as fluid\n",
    "from paddle.fluid.dygraph.nn import Conv2D, Pool2D, Linear, BatchNorm\n",
    "import math\n",
    "\n",
    "# 模组结构：输入维度，输出维度，滑动步长，基础长度, 队列长度\n",
    "group_arch = [(3, 256, 1, 2, 3), (256, 512, 2, 2, 3), (512, 1024, 2, 2, 3)]\n",
    "group_dim  = 1024 # 模组输出维度\n",
    "class_dim  = 10   # 类别数量维度\n",
    "\n",
    "# 卷积单元\n",
    "class ConvUnit(fluid.dygraph.Layer):\n",
    "    def __init__(self, in_dim, out_dim, filter_size=3, stride=1, act=None):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            初始化卷积单元，H/W=(H/W+2*P-F)/S+1\n",
    "        输入:\n",
    "            in_dim      - 输入维度\n",
    "            out_dim     - 输出维度\n",
    "            filter_size - 卷积大小\n",
    "            stride      - 滑动步长\n",
    "            act         - 激活函数\n",
    "        输出:\n",
    "        \"\"\"\n",
    "        super(ConvUnit, self).__init__()\n",
    "        \n",
    "        # 添加卷积\n",
    "        self.conv = Conv2D(\n",
    "            num_channels=in_dim,\n",
    "            num_filters=out_dim,\n",
    "            filter_size=filter_size,\n",
    "            stride=stride,\n",
    "            padding=(filter_size-1)//2,                       # 输出特征图大小不变\n",
    "            param_attr=fluid.initializer.MSRA(uniform=False), # 使用MARA 初始权重\n",
    "            bias_attr=False,                                  # 卷积输出没有偏置项\n",
    "            act=None)\n",
    "        \n",
    "        # 添加正则\n",
    "        self.norm = BatchNorm(\n",
    "            num_channels=out_dim,\n",
    "            param_attr=fluid.initializer.Constant(1.0), # 使用常量初始化权重\n",
    "            bias_attr=fluid.initializer.Constant(0.0),  # 使用常量初始化偏置\n",
    "            act=act)\n",
    "    \n",
    "    def forward(self, x):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            对输入的特征进行卷积和正则\n",
    "        输入:\n",
    "            x - 输入特征\n",
    "        输出:\n",
    "            x - 输出特征\n",
    "        \"\"\"\n",
    "        # 进行卷积\n",
    "        x = self.conv(x)\n",
    "        \n",
    "        # 进行正则\n",
    "        x = self.norm(x)\n",
    "        \n",
    "        return x\n",
    "\n",
    "# 投影单元\n",
    "class ProjUnit(fluid.dygraph.Layer):\n",
    "    def __init__(self, in_dim, out_dim, filter_size=1, stride=1, act=None):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            初始化投影单元，H/W=(H/W+2*P-F)/S+1\n",
    "        输入:\n",
    "            in_dim      - 输入维度\n",
    "            out_dim     - 输出维度\n",
    "            filter_size - 卷积大小\n",
    "            stride      - 滑动步长\n",
    "            act         - 激活函数\n",
    "        输出:\n",
    "        \"\"\"\n",
    "        super(ProjUnit, self).__init__()\n",
    "        \n",
    "        # 添加池化\n",
    "        self.pool = Pool2D(\n",
    "            pool_size=filter_size,\n",
    "            pool_stride=stride,\n",
    "            pool_padding=0,\n",
    "            pool_type='avg')\n",
    "        \n",
    "        # 添加卷积\n",
    "        self.conv = Conv2D(\n",
    "            num_channels=in_dim,\n",
    "            num_filters=out_dim,\n",
    "            filter_size=1,\n",
    "            stride=1,\n",
    "            padding=0,\n",
    "            param_attr=fluid.initializer.MSRA(uniform=False), # 使用MARA 初始权重\n",
    "            bias_attr=False,                                  # 卷积输出没有偏置项\n",
    "            act=None)\n",
    "        \n",
    "        # 添加正则\n",
    "        self.norm = BatchNorm(\n",
    "            num_channels=out_dim,\n",
    "            param_attr=fluid.initializer.Constant(1.0), # 使用常量初始化权重\n",
    "            bias_attr=fluid.initializer.Constant(0.0),  # 使用常量初始化偏置\n",
    "            act=act)\n",
    "    \n",
    "    def forward(self, x):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            对输入的特征进行池化卷积和正则\n",
    "        输入:\n",
    "            x - 输入特征\n",
    "        输出:\n",
    "            x - 输出特征\n",
    "        \"\"\"\n",
    "        # 进行池化\n",
    "        x = self.pool(x)\n",
    "        \n",
    "        # 进行卷积\n",
    "        x = self.conv(x)\n",
    "        \n",
    "        # 进行正则\n",
    "        x = self.norm(x)\n",
    "        \n",
    "        return x\n",
    "\n",
    "# 队列结构\n",
    "class SSRQueue(fluid.dygraph.Layer):\n",
    "    def __init__(self, in_dim, out_dim, stride=1, queues=2, act=None):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            初始化队列结构，H/W=(H/W+2*P-F)/S+1\n",
    "        输入:\n",
    "            in_dim  - 输入维度\n",
    "            out_dim - 输出维度\n",
    "            stride  - 滑动步长，1保持不变，2下采样\n",
    "            queues  - 队列长度，分割尺度为2^(n-1)\n",
    "            act     - 激活函数\n",
    "        输出:\n",
    "        \"\"\"\n",
    "        super(SSRQueue, self).__init__()\n",
    "        \n",
    "        # 添加队列变量\n",
    "        self.queues = queues # 队列长度\n",
    "        self.split_list = [] # 分割列表\n",
    "        \n",
    "        # 添加队列列表\n",
    "        self.queue_list = [] # 队列列表\n",
    "        for i in range(queues):\n",
    "            # 添加队列项目\n",
    "            queue_item = self.add_sublayer( # 构造队列项目\n",
    "                'queue_' + str(i),\n",
    "                ConvUnit(\n",
    "                    in_dim=(in_dim if i==0 else out_dim), # 每组队列项目除第一个外，in_dim=out_dim\n",
    "                    out_dim=out_dim,\n",
    "                    filter_size=3,\n",
    "                    stride=(stride if i==0 else 1), # 每组队列项目除第一块外，stride=1\n",
    "                    act=act))\n",
    "            self.queue_list.append(queue_item) # 添加队列项目\n",
    "            \n",
    "            # 计算输出维度\n",
    "            if i < (queues-1): # 如果不是最后一项\n",
    "                out_dim = out_dim//2 # 输出维度减半\n",
    "                self.split_list.append(out_dim) # 添加分割列表\n",
    "            \n",
    "    def forward(self, x):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            对输入的特征图像提取特征\n",
    "        输入:\n",
    "            x - 输入特征\n",
    "        输出:\n",
    "            x - 输出特征\n",
    "        \"\"\"\n",
    "        # 提取特征\n",
    "        x_list = [] # 队列输出列表\n",
    "        for i, queue_item in enumerate(self.queue_list):\n",
    "            if i < (self.queues-1): # 如果不是最后一项\n",
    "                x = queue_item(x) # 提取队列特征\n",
    "                x_item, x = fluid.layers.split(input=x, num_or_sections=[-1, self.split_list[i]], dim=1)\n",
    "                x_list.append(x_item) # 添加输出列表\n",
    "            else: # 否则不对特征分割\n",
    "                x = queue_item(x) # 提取队列特征\n",
    "                x_list.append(x) # 添加输出列表\n",
    "        \n",
    "        # 联结特征\n",
    "        x = fluid.layers.concat(input=x_list, axis=1) # 队列输出列表按通道维进行特征联结\n",
    "        \n",
    "        return x\n",
    "    \n",
    "# 基础结构\n",
    "class SSRBasic(fluid.dygraph.Layer):\n",
    "    def __init__(self, in_dim, out_dim, stride=1, queues=1, is_pass=True):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            初始化基础结构，H/W=(H/W+2*P-F)/S+1\n",
    "        输入:\n",
    "            in_dim  - 输入维度\n",
    "            out_dim - 输出维度\n",
    "            stride  - 滑动步长\n",
    "            queues  - 队列长度\n",
    "            is_pass - 是否直连\n",
    "        输出:\n",
    "        \"\"\"\n",
    "        super(SSRBasic, self).__init__()\n",
    "        \n",
    "        # 是否直连标识\n",
    "        self.is_pass = is_pass\n",
    "        \n",
    "        # 添加投影路径\n",
    "        self.proj = ProjUnit(in_dim=in_dim, out_dim=out_dim, filter_size=stride, stride=stride, act=None)\n",
    "        \n",
    "        # 添加卷积路径\n",
    "        if queues==1:\n",
    "            self.conv = ConvUnit(in_dim=in_dim, out_dim=out_dim, filter_size=3, stride=stride, act='relu')\n",
    "        else:\n",
    "            self.conv = SSRQueue(in_dim=in_dim, out_dim=out_dim, stride=stride, queues=queues, act='relu')\n",
    "        \n",
    "    def forward(self, x):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            对输入的特征图像提取特征\n",
    "        输入:\n",
    "            x - 输入特征\n",
    "        输出:\n",
    "            x - 输出特征\n",
    "            y - 输出特征\n",
    "        \"\"\"\n",
    "        # 直连路径\n",
    "        if self.is_pass: # 是否直连\n",
    "            x_pass = x\n",
    "        else:            # 否则投影\n",
    "            x_pass = self.proj(x)\n",
    "        \n",
    "        # 卷积路径\n",
    "        x_conv = self.conv(x)\n",
    "        \n",
    "        # 输出特征\n",
    "        x = fluid.layers.elementwise_add(x=x_pass, y=x_conv, act=None) # 直连路径与卷积路径进行特征相加\n",
    "        y = x\n",
    "        \n",
    "        return x, y\n",
    "    \n",
    "# 模块结构\n",
    "class SSRBlock(fluid.dygraph.Layer):\n",
    "    def __init__(self, in_dim, out_dim, stride=1, basics=1, queues=1):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            初始化模块结构，H/W=(H/W+2*P-F)/S+1\n",
    "        输入:\n",
    "            in_dim  - 输入维度\n",
    "            out_dim - 输出维度\n",
    "            stride  - 滑动步长\n",
    "            basics  - 基础长度\n",
    "            queues  - 队列长度\n",
    "        输出:\n",
    "        \"\"\"\n",
    "        super(SSRBlock, self).__init__()\n",
    "        \n",
    "        # 添加模块列表\n",
    "        self.block_list = [] # 模块列表\n",
    "        for i in range(basics):\n",
    "            block_item = self.add_sublayer( # 构造模块项目\n",
    "                'block_' + str(i),\n",
    "                SSRBasic(\n",
    "                    in_dim=(in_dim if i==0 else out_dim), # 每组模块项目除第一块外，输入维度=输出维度\n",
    "                    out_dim=out_dim,\n",
    "                    stride=(stride if i==0 else 1), # 每组模块项目除第一块外，stride=1\n",
    "                    queues=queues,\n",
    "                    is_pass=(False if i==0 else True))) # 每组模块项目除第一块外，is_pass=True\n",
    "            self.block_list.append(block_item) # 添加模块项目\n",
    "    \n",
    "    def forward(self, x):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            对输入的特征图像提取特征\n",
    "        输入:\n",
    "            x      - 输入特征\n",
    "        输出:\n",
    "            x      - 输出特征\n",
    "            y_list - 输出特征列表\n",
    "        \"\"\"\n",
    "        y_list = [] # 模块输出列表\n",
    "        for block_item in self.block_list:\n",
    "            x, y_item = block_item(x) # 提取模块特征\n",
    "            y_list.append(y_item) # 添加输出列表\n",
    "            \n",
    "        return x, y_list\n",
    "\n",
    "# 模组结构\n",
    "class SSRGroup(fluid.dygraph.Layer):\n",
    "    def __init__(self):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            初始化模组结构，H/W=(H/W+2*P-F)/S+1\n",
    "        输入:\n",
    "        输出:\n",
    "        \"\"\"\n",
    "        super(SSRGroup, self).__init__()\n",
    "        \n",
    "        # 添加模组列表\n",
    "        self.group_list = [] # 模组列表\n",
    "        for i, block_arch in enumerate(group_arch):\n",
    "            group_item = self.add_sublayer( # 构造模组项目\n",
    "                'group_' + str(i),\n",
    "                SSRBlock(\n",
    "                    in_dim=block_arch[0],\n",
    "                    out_dim=block_arch[1],\n",
    "                    stride=block_arch[2],\n",
    "                    basics=block_arch[3],\n",
    "                    queues=block_arch[4]))\n",
    "            self.group_list.append(group_item) # 添加模组项目\n",
    "    \n",
    "    def forward(self, x):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            对输入的特征图像提取特征\n",
    "        输入:\n",
    "            x      - 输入特征\n",
    "        输出:\n",
    "            x      - 输出特征\n",
    "            y_list - 输出特征列表\n",
    "        \"\"\"\n",
    "        y_list = [] # 模组输出列表\n",
    "        for group_item in self.group_list:\n",
    "            x, y_item = group_item(x) # 提取模组特征\n",
    "            y_list.append(y_item) # 添加输出列表\n",
    "            \n",
    "        return x, y_list\n",
    "        \n",
    "# 分割网络\n",
    "class SSRNet(fluid.dygraph.Layer):\n",
    "    def __init__(self):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            初始化分割网络，H/W=(H/W+2*P-F)/S+1\n",
    "        输入:\n",
    "        输出:\n",
    "        \"\"\"\n",
    "        super(SSRNet, self).__init__()\n",
    "        \n",
    "        # 添加模组结构\n",
    "        self.backbone = SSRGroup() # 输出：N*C*H*W\n",
    "        \n",
    "        # 添加全连接层\n",
    "        self.pool = Pool2D(global_pooling=True, pool_type='avg') # 输出：N*C*1*1\n",
    "        \n",
    "        stdv = 1.0/(math.sqrt(group_dim)*1.0)                    # 设置均匀分布权重方差\n",
    "        self.fc = Linear(                                        # 输出：=N*10\n",
    "            input_dim=group_dim,\n",
    "            output_dim=class_dim,\n",
    "            param_attr=fluid.initializer.Uniform(-stdv, stdv),   # 使用均匀分布初始权重\n",
    "            bias_attr=fluid.initializer.Constant(0.0),           # 使用常量数值初始偏置\n",
    "            act='softmax')\n",
    "    \n",
    "    def forward(self, x):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            对输入图像进行分类\n",
    "        输入:\n",
    "            x - 输入图像\n",
    "        输出:\n",
    "            x - 预测结果\n",
    "        \"\"\"\n",
    "        # 提取特征\n",
    "        x, y_list = self.backbone(x)\n",
    "        \n",
    "        # 进行预测\n",
    "        x = self.pool(x)\n",
    "        x = fluid.layers.reshape(x, [x.shape[0], -1])\n",
    "        x = self.fc(x)\n",
    "        \n",
    "        return x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tatol param: 28114954\n",
      "infer shape: [1, 10]\n"
     ]
    }
   ],
   "source": [
    "import paddle.fluid as fluid\n",
    "from paddle.fluid.dygraph.base import to_variable\n",
    "import numpy as np\n",
    "\n",
    "with fluid.dygraph.guard():\n",
    "    # 输入数据\n",
    "    x = np.random.randn(1, 3, 32, 32).astype(np.float32)\n",
    "    x = to_variable(x)\n",
    "    \n",
    "    # 进行预测\n",
    "    backbone = SSRNet() # 设置网络\n",
    "    \n",
    "    infer = backbone(x) # 进行预测\n",
    "    \n",
    "    # 显示结果\n",
    "    parameters = 0\n",
    "    for p in backbone.parameters():\n",
    "        parameters += np.prod(p.shape) # 统计参数\n",
    "    \n",
    "    print('tatol param:', parameters)\n",
    "    print('infer shape:', infer.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "### 训练模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "iteration: 109700, epoch: 281, train loss: 0.282821, valid loss: 0.178864, valid accuracy: 96.12%\r"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXcAAAD8CAYAAACMwORRAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4zLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvIxREBQAAIABJREFUeJztnXl8FdX5/98nNyvZ2MIalEVkRwREEFGpC6BV6lKrxbXfarWutf5atK21ihW1WrVFq1W01gW3qggoooLghoAg+xICSFhDCNnXm/P748zcmbvl3oRAcuPzfr3yyszcMzNncuEzz3me5zxHaa0RBEEQWhdxzd0BQRAEoekRcRcEQWiFiLgLgiC0QkTcBUEQWiEi7oIgCK0QEXdBEIRWiIi7IAhCK0TEXRAEoRUi4i4IgtAKiW+uG3fs2FH37NmzuW4vCIIQk6xYseKA1jorUrtmE/eePXuyfPny5rq9IAhCTKKU2hFNO3HLCIIgtEJE3AVBEFohIu6CIAitkGbzuYeipqaGvLw8Kisrm7srMU9ycjLZ2dkkJCQ0d1cEQWgGWpS45+XlkZ6eTs+ePVFKNXd3YhatNQUFBeTl5dGrV6/m7o4gCM1Ai3LLVFZW0qFDBxH2w0QpRYcOHWQEJAg/YFqUuAMi7E2E/B0F4YdNixP3SGitOVhWTV2dLA8oCIIQjpgT95LKWvIKy9lb3PQuh4KCAoYNG8awYcPo0qUL3bt39+1XV1dHdY1rr72WTZs2RX3P5557jttvv72xXRYEQQhJiwqoRoNtr1fX1jX5tTt06MCqVasAuPfee0lLS+POO+/0v7/WaK2Jiwv9XnzhhReavF+CIAgNJeYs9zjLlVynj55bJicnh4EDBzJlyhQGDRrEnj17uP766xk5ciSDBg3ivvvu87U99dRTWbVqFbW1tbRt25apU6dywgknMGbMGPbv31/vfbZt28b48eMZOnQoZ599Nnl5eQDMmjWLwYMHc8IJJzB+/HgA1qxZw0knncSwYcMYOnQoubm5R+4PIAhCzNFiLfe/vL+O9buLg457taay2osnTpGc4GnQNQd2y+DP5w9qVH82btzISy+9xMiRIwGYPn067du3p7a2lvHjx3PJJZcwcOBAv3OKioo4/fTTmT59OnfccQczZ85k6tSpYe/x61//ml/+8pdMmTKFZ599lttvv5233nqLv/zlLyxatIjOnTtz6NAhAJ566inuvPNOfvazn1FVVYU+ii87QRBaPjFnuds5IEdbyvr06eMTdoDXXnuN4cOHM3z4cDZs2MD69euDzklJSWHSpEkAjBgxgu3bt9d7j6VLl3LZZZcBcNVVV7FkyRIAxo4dy1VXXcVzzz1HXZ1xR51yyilMmzaNhx9+mJ07d5KcnNwUjykIQiuhxVru4SzsiupatuwvJSneQ78u6UetP6mpqb7tLVu28MQTT/DNN9/Qtm1brrjiipA55YmJib5tj8dDbW1to+7973//m6VLlzJnzhyGDx/OypUrufLKKxkzZgxz585l4sSJzJw5k9NOO61R1xcEofURc5a7TXO6IYqLi0lPTycjI4M9e/Ywf/78Jrnu6NGjeeONNwB4+eWXfWKdm5vL6NGjuf/++2nXrh27du0iNzeX4447jttuu40f//jHrF69ukn6IAhC66DFWu7hsCW96XNlomf48OEMHDiQ/v37c+yxxzJ27Ngmue6MGTP4xS9+wYMPPkjnzp19mTe/+c1v2LZtG1przjnnHAYPHsy0adN47bXXSEhIoFu3btx7771N0gdBEFoHqrks4JEjR+rAxTo2bNjAgAED6j2vvLqWnP2lxMfFMbBbxpHsYswTzd9TEITYQim1Qms9MlK7mHXLCIIgCOERcRcEQWiFiLgLgiC0QkTcBUEQWiEi7oIgCK0QEXdBEIRWSOyJ+xHM3Bw/fnzQhKTHH3+cG2+8sd7z0tLSANi9ezeXXHJJyDZnnHEGgamf9R0XBEE4HGJP3H00vcpffvnlzJo1y+/YrFmzuPzyy6M6v1u3brz11ltN3i9BEISGEnPifiSnXF1yySXMnTvXtzDH9u3b2b17N+PGjaO0tJQzzzyT4cOHM2TIEN57772g87dv387gwYMBqKio4LLLLmPAgAFceOGFVFRURLz/a6+9xpAhQxg8eDC///3vAfB6vVxzzTUMHjyYIUOG8Pe//x2AJ598koEDBzJ06FBfsTFBEASbllt+4IOpsHdN0OFkreld7UUpILGB3e8yBCZND/tx+/btGTVqFB988AGTJ09m1qxZXHrppSilSE5O5p133iEjI4MDBw4wevRoLrjggrBrlT799NO0adOGDRs2sHr1aoYPH15v13bv3s3vf/97VqxYQbt27TjnnHN49913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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "iteration: 109800, epoch: 281, train loss: 0.282660, valid loss: 0.178864, valid accuracy: 96.12%\n",
      "iteration: 109900, epoch: 282, train loss: 0.282699, valid loss: 0.178796, valid accuracy: 96.16%\n",
      "iteration: 110000, epoch: 282, train loss: 0.282624, valid loss: 0.178796, valid accuracy: 96.16%\n",
      "iteration: 110100, epoch: 282, train loss: 0.282708, valid loss: 0.178796, valid accuracy: 96.16%\n",
      "iteration: 110200, epoch: 282, train loss: 0.282603, valid loss: 0.178796, valid accuracy: 96.16%\n",
      "iteration: 110300, epoch: 283, train loss: 0.282715, valid loss: 0.178716, valid accuracy: 96.09%\n",
      "iteration: 110400, epoch: 283, train loss: 0.282623, valid loss: 0.178716, valid accuracy: 96.09%\n",
      "iteration: 110500, epoch: 283, train loss: 0.282699, valid loss: 0.178716, valid accuracy: 96.09%\n",
      "iteration: 110600, epoch: 283, train loss: 0.282844, valid loss: 0.178716, valid accuracy: 96.09%\n",
      "iteration: 110700, epoch: 284, train loss: 0.282810, valid loss: 0.178569, valid accuracy: 96.13%\n",
      "iteration: 110800, epoch: 284, train loss: 0.283059, valid loss: 0.178569, valid accuracy: 96.13%\n",
      "iteration: 110900, epoch: 284, train loss: 0.282988, valid loss: 0.178569, valid accuracy: 96.13%\n",
      "iteration: 111000, epoch: 284, train loss: 0.283455, valid loss: 0.178569, valid accuracy: 96.13%\n",
      "iteration: 111100, epoch: 285, train loss: 0.282659, valid loss: 0.178109, valid accuracy: 96.14%\n",
      "iteration: 111200, epoch: 285, train loss: 0.282816, valid loss: 0.178109, valid accuracy: 96.14%\n",
      "iteration: 111300, epoch: 285, train loss: 0.282645, valid loss: 0.178109, valid accuracy: 96.14%\n",
      "iteration: 111400, epoch: 285, train loss: 0.282817, valid loss: 0.178109, valid accuracy: 96.14%\n",
      "iteration: 111500, epoch: 286, train loss: 0.282675, valid loss: 0.179419, valid accuracy: 96.06%\n",
      "iteration: 111600, epoch: 286, train loss: 0.282719, valid loss: 0.179419, valid accuracy: 96.06%\n",
      "iteration: 111700, epoch: 286, train loss: 0.282743, valid loss: 0.179419, valid accuracy: 96.06%\n",
      "iteration: 111800, epoch: 286, train loss: 0.282601, valid loss: 0.179419, valid accuracy: 96.06%\n",
      "iteration: 111900, epoch: 287, train loss: 0.282929, valid loss: 0.177617, valid accuracy: 96.14%\n",
      "iteration: 112000, epoch: 287, train loss: 0.282755, valid loss: 0.177617, valid accuracy: 96.14%\n",
      "iteration: 112100, epoch: 287, train loss: 0.282678, valid loss: 0.177617, valid accuracy: 96.14%\n",
      "iteration: 112200, epoch: 287, train loss: 0.282866, valid loss: 0.177617, valid accuracy: 96.14%\n",
      "iteration: 112300, epoch: 288, train loss: 0.282592, valid loss: 0.178601, valid accuracy: 96.11%\n",
      "iteration: 112400, epoch: 288, train loss: 0.282679, valid loss: 0.178601, valid accuracy: 96.11%\n",
      "iteration: 112500, epoch: 288, train loss: 0.282690, valid loss: 0.178601, valid accuracy: 96.11%\n",
      "iteration: 112600, epoch: 288, train loss: 0.282614, valid loss: 0.178601, valid accuracy: 96.11%\n",
      "iteration: 112700, epoch: 289, train loss: 0.282601, valid loss: 0.178278, valid accuracy: 96.14%\n",
      "iteration: 112800, epoch: 289, train loss: 0.282667, valid loss: 0.178278, valid accuracy: 96.14%\n",
      "iteration: 112900, epoch: 289, train loss: 0.282790, valid loss: 0.178278, valid accuracy: 96.14%\n",
      "iteration: 113000, epoch: 290, train loss: 0.283111, valid loss: 0.179006, valid accuracy: 96.06%\n",
      "iteration: 113100, epoch: 290, train loss: 0.282637, valid loss: 0.179006, valid accuracy: 96.06%\n",
      "iteration: 113200, epoch: 290, train loss: 0.282834, valid loss: 0.179006, valid accuracy: 96.06%\n",
      "iteration: 113300, epoch: 290, train loss: 0.282617, valid loss: 0.179006, valid accuracy: 96.06%\n",
      "iteration: 113400, epoch: 291, train loss: 0.282692, valid loss: 0.177850, valid accuracy: 96.11%\n",
      "iteration: 113500, epoch: 291, train loss: 0.282591, valid loss: 0.177850, valid accuracy: 96.11%\n",
      "iteration: 113600, epoch: 291, train loss: 0.283341, valid loss: 0.177850, valid accuracy: 96.11%\n",
      "iteration: 113700, epoch: 291, train loss: 0.282962, valid loss: 0.177850, valid accuracy: 96.11%\n",
      "iteration: 113800, epoch: 292, train loss: 0.282910, valid loss: 0.178384, valid accuracy: 96.18%\n",
      "iteration: 113900, epoch: 292, train loss: 0.282634, valid loss: 0.178384, valid accuracy: 96.18%\n",
      "iteration: 114000, epoch: 292, train loss: 0.282760, valid loss: 0.178384, valid accuracy: 96.18%\n",
      "iteration: 114100, epoch: 292, train loss: 0.282643, valid loss: 0.178384, valid accuracy: 96.18%\n",
      "iteration: 114200, epoch: 293, train loss: 0.282664, valid loss: 0.179571, valid accuracy: 96.09%\n",
      "iteration: 114300, epoch: 293, train loss: 0.282852, valid loss: 0.179571, valid accuracy: 96.09%\n",
      "iteration: 114400, epoch: 293, train loss: 0.282599, valid loss: 0.179571, valid accuracy: 96.09%\n",
      "iteration: 114500, epoch: 293, train loss: 0.282643, valid loss: 0.179571, valid accuracy: 96.09%\n",
      "iteration: 114600, epoch: 294, train loss: 0.282869, valid loss: 0.177436, valid accuracy: 96.13%\n",
      "iteration: 114700, epoch: 294, train loss: 0.282759, valid loss: 0.177436, valid accuracy: 96.13%\n",
      "iteration: 114800, epoch: 294, train loss: 0.283070, valid loss: 0.177436, valid accuracy: 96.13%\n",
      "iteration: 114900, epoch: 294, train loss: 0.282818, valid loss: 0.177436, valid accuracy: 96.13%\n",
      "iteration: 115000, epoch: 295, train loss: 0.282612, valid loss: 0.178532, valid accuracy: 96.13%\n",
      "iteration: 115100, epoch: 295, train loss: 0.284378, valid loss: 0.178532, valid accuracy: 96.13%\n",
      "iteration: 115200, epoch: 295, train loss: 0.282667, valid loss: 0.178532, valid accuracy: 96.13%\n",
      "iteration: 115300, epoch: 295, train loss: 0.282605, valid loss: 0.178532, valid accuracy: 96.13%\n",
      "iteration: 115400, epoch: 296, train loss: 0.282813, valid loss: 0.178337, valid accuracy: 96.08%\n",
      "iteration: 115500, epoch: 296, train loss: 0.282799, valid loss: 0.178337, valid accuracy: 96.08%\n",
      "iteration: 115600, epoch: 296, train loss: 0.282686, valid loss: 0.178337, valid accuracy: 96.08%\n",
      "iteration: 115700, epoch: 296, train loss: 0.282660, valid loss: 0.178337, valid accuracy: 96.08%\n",
      "iteration: 115800, epoch: 297, train loss: 0.282543, valid loss: 0.178581, valid accuracy: 96.12%\n",
      "iteration: 115900, epoch: 297, train loss: 0.282806, valid loss: 0.178581, valid accuracy: 96.12%\n",
      "iteration: 116000, epoch: 297, train loss: 0.282869, valid loss: 0.178581, valid accuracy: 96.12%\n",
      "iteration: 116100, epoch: 297, train loss: 0.283324, valid loss: 0.178581, valid accuracy: 96.12%\n",
      "iteration: 116200, epoch: 298, train loss: 0.282928, valid loss: 0.178196, valid accuracy: 96.11%\n",
      "iteration: 116300, epoch: 298, train loss: 0.282662, valid loss: 0.178196, valid accuracy: 96.11%\n",
      "iteration: 116400, epoch: 298, train loss: 0.282828, valid loss: 0.178196, valid accuracy: 96.11%\n",
      "iteration: 116500, epoch: 298, train loss: 0.282664, valid loss: 0.178196, valid accuracy: 96.11%\n",
      "iteration: 116600, epoch: 299, train loss: 0.282817, valid loss: 0.178639, valid accuracy: 96.07%\n",
      "iteration: 116700, epoch: 299, train loss: 0.282732, valid loss: 0.178639, valid accuracy: 96.07%\n",
      "iteration: 116800, epoch: 299, train loss: 0.282735, valid loss: 0.178639, valid accuracy: 96.07%\n",
      "iteration: 116900, epoch: 299, train loss: 0.282798, valid loss: 0.178639, valid accuracy: 96.07%\n",
      "iteration: 117000, epoch: 300, train loss: 0.282764, valid loss: 0.178034, valid accuracy: 96.18%\n",
      "iteration: 117100, epoch: 300, train loss: 0.283675, valid loss: 0.178034, valid accuracy: 96.18%\n",
      "iteration: 117200, epoch: 300, train loss: 0.282807, valid loss: 0.178034, valid accuracy: 96.18%\n",
      "iteration: 117300, epoch: 300, train loss: 0.282837, valid loss: 0.178034, valid accuracy: 96.18%\n",
      "complete - train time: 23767s, best epoch: 293, best loss: 0.177436, best accuracy: 96.13%\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<Figure size 432x288 with 0 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import paddle\n",
    "import paddle.fluid as fluid\n",
    "from paddle.utils.plot import Ploter\n",
    "import numpy as np\n",
    "import time\n",
    "import math\n",
    "import os\n",
    "\n",
    "epoch_num = 300   # 训练周期，取值一般为[1,300]\n",
    "train_batch = 128 # 训练批次，取值一般为[1,256]\n",
    "valid_batch = 128 # 验证批次，取值一般为[1,256]\n",
    "displays = 100    # 显示迭代\n",
    "\n",
    "start_lr = 0.00001                         # 开始学习率，取值一般为[1e-8,5e-1]\n",
    "based_lr = 0.1                             # 基础学习率，取值一般为[1e-8,5e-1]\n",
    "epoch_iters = math.ceil(50000/train_batch) # 每轮迭代数\n",
    "warmup_iter = 10 * epoch_iters             # 预热迭代数，取值一般为[1,10]\n",
    "\n",
    "momentum = 0.9     # 优化器动量\n",
    "l2_decay = 0.00005 # 正则化系数，取值一般为[1e-5,5e-4]\n",
    "epsilon = 0.05     # 标签平滑率，取值一般为[1e-2,1e-1]\n",
    "\n",
    "checkpoint = True                    # 断点标识\n",
    "model_path = './work/out/ssrnet'     # 模型路径\n",
    "result_txt = './work/out/result.txt' # 结果文件\n",
    "class_num  = 10                      # 类别数量\n",
    "\n",
    "with fluid.dygraph.guard():\n",
    "    # 准备数据\n",
    "    train_reader = paddle.batch(\n",
    "        reader=paddle.reader.shuffle(reader=paddle.dataset.cifar.train10(), buf_size=50000),\n",
    "        batch_size=train_batch)\n",
    "    \n",
    "    valid_reader = paddle.batch(\n",
    "        reader=paddle.dataset.cifar.test10(),\n",
    "        batch_size=valid_batch)\n",
    "    \n",
    "    # 声明模型\n",
    "    model = SSRNet()\n",
    "    \n",
    "    # 优化算法\n",
    "    consine_lr = fluid.layers.cosine_decay(based_lr, epoch_iters, epoch_num) # 余弦衰减策略\n",
    "    decayed_lr = fluid.layers.linear_lr_warmup(consine_lr, warmup_iter, start_lr, based_lr) # 线性预热策略\n",
    "    \n",
    "    optimizer = fluid.optimizer.Momentum(\n",
    "        learning_rate=decayed_lr,                           # 衰减学习策略\n",
    "        momentum=momentum,                                  # 优化动量系数\n",
    "        regularization=fluid.regularizer.L2Decay(l2_decay), # 正则衰减系数\n",
    "        parameter_list=model.parameters())\n",
    "    \n",
    "    # 加载断点\n",
    "    if checkpoint: # 是否加载断点文件\n",
    "        model_dict, optimizer_dict = fluid.load_dygraph(model_path) # 加载断点参数\n",
    "        model.set_dict(model_dict)                                  # 设置权重参数\n",
    "        optimizer.set_dict(optimizer_dict)                          # 设置优化参数\n",
    "    else:          # 否则删除结果文件\n",
    "        if os.path.exists(result_txt): # 如果存在结果文件\n",
    "            os.remove(result_txt)      # 那么删除结果文件\n",
    "    \n",
    "    # 初始训练\n",
    "    avg_train_loss = 0 # 平均训练损失\n",
    "    avg_valid_loss = 0 # 平均验证损失\n",
    "    avg_valid_accu = 0 # 平均验证精度\n",
    "    \n",
    "    iterator = 1                                # 迭代次数\n",
    "    train_prompt = \"Train loss\"                 # 训练标签\n",
    "    valid_prompt = \"Valid loss\"                 # 验证标签\n",
    "    ploter = Ploter(train_prompt, valid_prompt) # 训练图像\n",
    "    \n",
    "    best_epoch = 0           # 最好周期\n",
    "    best_accu = 0            # 最好精度\n",
    "    best_loss = 100.0        # 最好损失\n",
    "    train_time = time.time() # 训练时间\n",
    "    \n",
    "    # 开始训练\n",
    "    for epoch_id in range(epoch_num):\n",
    "        # 训练模型\n",
    "        model.train() # 设置训练\n",
    "        for batch_id, train_data in enumerate(train_reader()):\n",
    "            # 读取数据\n",
    "            image_data = np.array([x[0] for x in train_data]).reshape((-1, 3, 32, 32)).astype(np.float32) # 读取图像数据\n",
    "            image_data = train_augment(image_data)                                                        # 使用数据增强\n",
    "            image = fluid.dygraph.to_variable(image_data)                                                 # 转换数据类型\n",
    "\n",
    "            label_data = np.array([x[1] for x in train_data]).astype(np.int64)                        # 读取标签数据\n",
    "            label = fluid.dygraph.to_variable(label_data)                                             # 转换数据类型\n",
    "            label = fluid.layers.label_smooth(label=fluid.one_hot(label, class_num), epsilon=epsilon) # 使用标签平滑\n",
    "            label.stop_gradient = True                                                                # 停止梯度传播\n",
    "\n",
    "            # 前向传播\n",
    "            infer = model(image)\n",
    "            \n",
    "            # 计算损失\n",
    "            loss = fluid.layers.cross_entropy(infer, label, soft_label=True)\n",
    "            train_loss = fluid.layers.mean(loss)\n",
    "            \n",
    "            # 反向传播\n",
    "            train_loss.backward()\n",
    "            optimizer.minimize(train_loss)\n",
    "            model.clear_gradients()\n",
    "            \n",
    "            # 显示结果\n",
    "            if iterator % displays == 0:\n",
    "                # 显示图像\n",
    "                avg_train_loss = train_loss.numpy()[0]                # 设置训练损失\n",
    "                ploter.append(train_prompt, iterator, avg_train_loss) # 添加训练图像\n",
    "                ploter.plot()                                         # 显示训练图像\n",
    "                \n",
    "                # 打印结果\n",
    "                print(\"iteration: {:6d}, epoch: {:3d}, train loss: {:.6f}, valid loss: {:.6f}, valid accuracy: {:.2%}\".format(\n",
    "                    iterator, epoch_id+1, avg_train_loss, avg_valid_loss, avg_valid_accu))\n",
    "                \n",
    "                # 写入文件\n",
    "                with open(result_txt, 'a') as file:\n",
    "                    file.write(\"iteration: {:6d}, epoch: {:3d}, train loss: {:.6f}, valid loss: {:.6f}, valid accuracy: {:.2%}\\n\".format(\n",
    "                        iterator, epoch_id+1, avg_train_loss, avg_valid_loss, avg_valid_accu))\n",
    "            \n",
    "            # 增加迭代\n",
    "            iterator += 1\n",
    "            \n",
    "        # 验证模型\n",
    "        valid_loss_list = [] # 验证损失列表\n",
    "        valid_accu_list = [] # 验证精度列表\n",
    "        \n",
    "        model.eval()   # 设置验证\n",
    "        for batch_id, valid_data in enumerate(valid_reader()):\n",
    "            # 读取数据\n",
    "            image_data = np.array([x[0] for x in valid_data]).reshape((-1, 3, 32, 32)).astype(np.float32) # 读取图像数据\n",
    "            image_data = valid_augment(image_data)                                                        # 使用图像增强\n",
    "            image = fluid.dygraph.to_variable(image_data)                                                 # 转换数据类型\n",
    "            \n",
    "            label_data = np.array([x[1] for x in valid_data]).reshape((-1, 1)).astype(np.int64) # 读取标签数据\n",
    "            label = fluid.dygraph.to_variable(label_data)                                       # 转换数据类型\n",
    "            label.stop_gradient = True                                                          # 停止梯度传播\n",
    "            \n",
    "            # 前向传播\n",
    "            infer = model(image)\n",
    "            \n",
    "            # 计算精度\n",
    "            valid_accu = fluid.layers.accuracy(infer,label)\n",
    "            \n",
    "            valid_accu_list.append(valid_accu.numpy())\n",
    "            \n",
    "            # 计算损失\n",
    "            loss = fluid.layers.cross_entropy(infer, label)\n",
    "            valid_loss = fluid.layers.mean(loss)\n",
    "            \n",
    "            valid_loss_list.append(valid_loss.numpy())\n",
    "        \n",
    "        # 设置结果\n",
    "        avg_valid_accu = np.mean(valid_accu_list)             # 设置验证精度\n",
    "        \n",
    "        avg_valid_loss = np.mean(valid_loss_list)             # 设置验证损失\n",
    "        ploter.append(valid_prompt, iterator, avg_valid_loss) # 添加训练图像\n",
    "        \n",
    "        # 保存模型\n",
    "        fluid.save_dygraph(model.state_dict(), model_path)     # 保存权重参数\n",
    "        fluid.save_dygraph(optimizer.state_dict(), model_path) # 保存优化参数\n",
    "        \n",
    "        if avg_valid_loss < best_loss:\n",
    "            fluid.save_dygraph(model.state_dict(), model_path + '-best') # 保存权重\n",
    "            \n",
    "            best_epoch = epoch_id + 1                                    # 更新迭代\n",
    "            best_accu = avg_valid_accu                                   # 更新精度\n",
    "            best_loss = avg_valid_loss                                   # 更新损失\n",
    "    \n",
    "    # 显示结果\n",
    "    train_time = time.time() - train_time # 设置训练时间\n",
    "    print('complete - train time: {:.0f}s, best epoch: {:3d}, best loss: {:.6f}, best accuracy: {:.2%}'.format(\n",
    "        train_time, best_epoch, best_loss, best_accu))\n",
    "    \n",
    "    # 写入文件\n",
    "    with open(result_txt, 'a') as file:\n",
    "        file.write('complete - train time: {:.0f}s, best epoch: {:3d}, best loss: {:.6f}, best accuracy: {:.2%}\\n'.format(\n",
    "            train_time, best_epoch, best_loss, best_accu))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "### 模型预测"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "infer time: 0.010535s, infer value: horse\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 216x216 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import paddle.fluid as fluid\n",
    "from PIL import Image\n",
    "import numpy as np\n",
    "import time\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "image_path = './work/out/img.png' # 图片路径\n",
    "model_path = './work/out/ssrnet-best' # 模型路径\n",
    "\n",
    "# 加载图像\n",
    "def load_image(image_path):\n",
    "    \"\"\"\n",
    "    功能:\n",
    "        读取图像并转换到输入格式\n",
    "    输入:\n",
    "        image_path - 输入图像路径\n",
    "    输出:\n",
    "        image - 输出图像\n",
    "    \"\"\"\n",
    "    # 读取图像\n",
    "    image = Image.open(image_path) # 打开图像文件\n",
    "    \n",
    "    # 转换格式\n",
    "    image = image.resize((32, 32), Image.ANTIALIAS) # 调整图像大小\n",
    "    image = np.array(image, dtype=np.float32) # 转换数据格式，数据类型转换为float32\n",
    "\n",
    "    # 减去均值\n",
    "    mean = np.array([0.4914, 0.4822, 0.4465]).reshape((1, 1, -1)) # cifar数据集通道平均值\n",
    "    stdv = np.array([0.2471, 0.2435, 0.2616]).reshape((1, 1, -1)) # cifar数据集通道标准差\n",
    "    \n",
    "    image = (image/255.0 - mean) / stdv # 对图像进行归一化\n",
    "    image = image.transpose((2, 0, 1)).astype(np.float32) # 数据格式从HWC转换为CHW，数据类型转换为float32\n",
    "    \n",
    "    # 增加维度\n",
    "    image = np.expand_dims(image, axis=0) # 增加数据维度\n",
    "    \n",
    "    return image\n",
    "\n",
    "# 预测图像\n",
    "with fluid.dygraph.guard():\n",
    "    # 读取图像\n",
    "    image = load_image(image_path)\n",
    "    image = fluid.dygraph.to_variable(image)\n",
    "    \n",
    "    # 加载模型\n",
    "    model = SSRNet()                               # 加载模型\n",
    "    model_dict, _ = fluid.load_dygraph(model_path) # 加载权重\n",
    "    model.set_dict(model_dict)                     # 设置权重\n",
    "    model.eval()                                   # 设置验证\n",
    "    \n",
    "    # 前向传播\n",
    "    infer_time = time.time()              # 推断开始时间\n",
    "    infer = model(image)\n",
    "    infer_time = time.time() - infer_time # 推断结束时间\n",
    "    \n",
    "    # 显示结果\n",
    "    vlist = [\"airplane\", \"automobile\", \"bird\", \"cat\", \"deer\", \"dog\", \"frog\", \"horse\", \"ship\", \"truck\"] # 标签名称列表\n",
    "    print('infer time: {:f}s, infer value: {}'.format(infer_time, vlist[np.argmax(infer.numpy())]) )\n",
    "    \n",
    "    image = Image.open(image_path) # 打开图像文件\n",
    "    plt.figure(figsize=(3, 3))     # 设置显示大小\n",
    "    plt.imshow(image)              # 设置显示图像\n",
    "    plt.show()                     # 显示图像文件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "PaddlePaddle 1.8.4 (Python 3.5)",
   "language": "python",
   "name": "py35-paddle1.2.0"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
